package org.budo.dubbo.protocol.http.aop.permission.user;

import java.io.Serializable;
import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.budo.dubbo.protocol.http.authentication.AuthenticationCheckService;
import org.budo.dubbo.protocol.http.util.BudoReflectionUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.springframework.core.annotation.Order;

import lombok.Setter;

/**
 * @author lmw
 */
@Setter
@Order(1010)
public class UserPermissionCheckMethodInterceptor implements MethodInterceptor {
    private AuthenticationCheckService authenticationCheckService;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        UserPermissionCheck annotation = BudoReflectionUtil.getMethodAnnotation(method, UserPermissionCheck.class);

        // 没有注解
        if (null == annotation || !(annotation instanceof UserPermissionCheck)) {
            return invocation.proceed();
        }

        // 有注解
        Serializable authenticationPrincipal = this.getAuthenticationCheckService().getAuthenticationPrincipal();

        String permissionName = this.permissionName(method);

        if (this.hasPermission(authenticationPrincipal, permissionName)) {
            // 权限检查通过
            return invocation.proceed();
        }

        throw new UserPermissionCheckException(authenticationPrincipal, permissionName);
    }

    /**
     * 可以继承扩展这个方法
     */
    protected boolean hasPermission(Serializable authenticationPrincipal, String permissionName) {
        return false;
    }

    /**
     * 权限名称
     */
    private String permissionName(Method method) {
        String methodName = method.getName();

        Class<?> declaringClass = method.getDeclaringClass();
        Class<?>[] interfaces = declaringClass.getInterfaces();

        for (Class<?> iface : interfaces) {
            if (ReflectUtil.hasMethod(iface, methodName, method.getParameterTypes())) {
                return iface.getSimpleName() + "." + methodName;
            }
        }

        return declaringClass.getSimpleName() + "." + methodName;
    }

    public AuthenticationCheckService getAuthenticationCheckService() {
        if (null == this.authenticationCheckService) {
            this.authenticationCheckService = BudoApplicationContextAware.getBean(AuthenticationCheckService.class);
        }
        return this.authenticationCheckService;
    }
}
